package org.jeecg.modules.tencent_esign.tencent_upload.utils;

import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.jeecg.modules.tencent_esign.tencent_upload.config.TencentEsignConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;
import java.util.TimeZone;
import java.util.UUID;

/**
 * @Description: 腾讯电子签工具类
 * @Author: jeecg-boot
 * @Date: 2025-06-28
 * @Version: V1.0
 */
@Slf4j
@Component
public class TencentEsignUtil {

    @Autowired
    private TencentEsignConfig tencentEsignConfig;

    /**
     * 生成签名
     *
     * @param action 操作名称
     * @param payload 请求参数
     * @return
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeyException
     */
    public String generateSignature(String action, String payload) throws NoSuchAlgorithmException, InvalidKeyException {
        String secretId = tencentEsignConfig.getAppId();
        String secretKey = tencentEsignConfig.getSecret();
        String service = "ess";
        String version = tencentEsignConfig.getVersion();
        String region = tencentEsignConfig.getRegion();

        // 获取当前时间戳 (使用UTC时间戳以避免时区问题)
        long timestamp = Instant.now().getEpochSecond();
        // 使用UTC时间格式化日期用于签名
        String date = LocalDateTime.ofInstant(Instant.ofEpochSecond(timestamp), ZoneOffset.UTC)
                .format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));

        // ************* 步骤 1：拼接规范请求串 *************
        String canonicalUri = "/";
        String canonicalQueryString = "";
        String canonicalHeaders = "content-type:application/json; charset=utf-8\nhost:" + getHost() + "\n";
        String signedHeaders = "content-type;host";
        String hashedRequestPayload = sha256Hex(payload);
        String canonicalRequest = "POST" + "\n" + canonicalUri + "\n" + canonicalQueryString + "\n"
                + canonicalHeaders + "\n" + signedHeaders + "\n" + hashedRequestPayload;

        // ************* 步骤 2：拼接待签名字符串 *************
        String algorithm = "TC3-HMAC-SHA256";
        String credentialScope = date + "/" + service + "/tc3_request";
        String hashedCanonicalRequest = sha256Hex(canonicalRequest);
        String stringToSign = algorithm + "\n" + timestamp + "\n" + credentialScope + "\n" + hashedCanonicalRequest;

        // ************* 步骤 3：计算签名 *************
        byte[] secretDate = hmac256(("TC3" + secretKey).getBytes(StandardCharsets.UTF_8), date);
        byte[] secretService = hmac256(secretDate, service);
        byte[] secretSigning = hmac256(secretService, "tc3_request");
        byte[] signature = hmac256(secretSigning, stringToSign);

        // ************* 步骤 4：拼接 Authorization *************
        String authorization = algorithm + " " + "Credential=" + secretId + "/" + credentialScope + ", "
                + "SignedHeaders=" + signedHeaders + ", " + "Signature=" + Base64.encodeBase64String(signature);

        return authorization;
    }

    /**
     * 构造请求头
     *
     * @param action 操作名称
     * @param payload 请求参数
     * @return
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeyException
     */
    public Map<String, String> buildHeaders(String action, String payload) throws NoSuchAlgorithmException, InvalidKeyException {
        Map<String, String> headers = new HashMap<>();
        headers.put("Authorization", generateSignature(action, payload));
        headers.put("Content-Type", "application/json; charset=utf-8");
        headers.put("Host", getHost());
        headers.put("X-TC-Action", action);
        // 使用UTC时间戳以避免时区问题
        headers.put("X-TC-Timestamp", String.valueOf(Instant.now().getEpochSecond()));
        headers.put("X-TC-Version", tencentEsignConfig.getVersion());
        headers.put("X-TC-Region", tencentEsignConfig.getRegion());
        return headers;
    }

    /**
     * 生成请求ID
     *
     * @return
     */
    public String generateRequestId() {
        return UUID.randomUUID().toString();
    }

    /**
     * SHA256加密
     *
     * @param s
     * @return
     */
    private String sha256Hex(String s) {
        try {
            MessageDigest md = MessageDigest.getInstance("SHA-256");
            byte[] d = md.digest(s.getBytes(StandardCharsets.UTF_8));
            return Base64.encodeBase64String(d);
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("SHA-256 algorithm not found", e);
        }
    }

    /**
     * HMAC-SHA256加密
     *
     * @param key
     * @param msg
     * @return
     * @throws InvalidKeyException
     * @throws NoSuchAlgorithmException
     */
    private byte[] hmac256(byte[] key, String msg) throws InvalidKeyException, NoSuchAlgorithmException {
        Mac mac = Mac.getInstance("HmacSHA256");
        SecretKeySpec secretKeySpec = new SecretKeySpec(key, mac.getAlgorithm());
        mac.init(secretKeySpec);
        return mac.doFinal(msg.getBytes(StandardCharsets.UTF_8));
    }

    /**
     * 获取主机地址
     *
     * @return
     */
    private String getHost() {
        String apiUrl = tencentEsignConfig.getApiUrl();
        if (apiUrl.startsWith("https://")) {
            return apiUrl.substring(8);
        } else if (apiUrl.startsWith("http://")) {
            return apiUrl.substring(7);
        }
        return apiUrl;
    }

    /**
     * 构造上传文件请求参数
     *
     * @param fileBase64 文件Base64编码
     * @param fileName 文件名
     * @param businessType 业务类型 (TEMPLATE: 模板, DOCUMENT: 文档, SEAL: 印章)
     * @param agent 代理信息
     * @return
     */
    public JSONObject buildUploadFileRequest(String fileBase64, String fileName, String businessType, JSONObject agent) {
        JSONObject params = new JSONObject();
        params.put("Agent", agent);
        params.put("BusinessType", businessType);
        params.put("FileInfos", new JSONObject[]{createFileInfo(fileBase64, fileName)});
        return params;
    }
    
    /**
     * 创建文件信息对象
     * 
     * @param fileBody 文件Base64内容
     * @param fileName 文件名
     * @return
     */
    private JSONObject createFileInfo(String fileBody, String fileName) {
        JSONObject fileInfo = new JSONObject();
        fileInfo.put("FileBody", fileBody);
        fileInfo.put("FileName", fileName);
        return fileInfo;
    }
}